﻿Namespace EIBData.DPT
    Public MustInherit Class EIBDataDPT_19_Base
        Inherits EIBDataDPT_Base

        Protected _year As UInt16 = Nothing
        Protected _month As Byte = Nothing
        Protected _day As Byte = Nothing
        Protected _dayOfWeek As Enums.EIBData_Days = Nothing
        Protected _hour As Byte = Nothing
        Protected _minute As Byte = Nothing
        Protected _second As Byte = Nothing
        Protected _fault As Enums.EIBData_DateFault = Nothing
        Protected _workingDay As Enums.EIBData_WorkingDay = Nothing
        Protected _workingDayValid As Enums.EIBData_Valid = Nothing
        Protected _yearValid As Enums.EIBData_Valid = Nothing
        Protected _monthDayValid As Enums.EIBData_Valid = Nothing
        Protected _dayOfWeekValid As Enums.EIBData_Valid = Nothing
        Protected _timeValid As Enums.EIBData_Valid = Nothing
        Protected _time As Enums.EIBData_Time = Nothing
        Protected _clockQuality As Enums.EIBData_ClockQuality = Nothing


        Sub New(data As Byte())
            If Not CanEncode(data) Then Throw New EIB.Exceptions.EIBDataDecodeException("")

            MyBase._data = data
            _year = 1900 + data(1)
            _month = (data(2) And &HF)
            _day = (data(3) And &H1F)
            _dayOfWeek = (data(4) And &HE0) >> 5
            _hour = (data(4) And &H1F)
            _minute = (data(5) And &H3F)
            _second = (data(6) And &H3F)
            _fault = (data(7) And &H80) >> 7
            _workingDay = (data(7) And &H40) >> 6
            _workingDayValid = (data(7) And &H20) >> 5
            _yearValid = (data(7) And &H10) >> 4
            _monthDayValid = (data(7) And &H8) >> 3
            _dayOfWeekValid = (data(7) And &H4) >> 2
            _timeValid = (data(7) And &H2) >> 1
            _time = (data(7) And &H1)
            _clockQuality = (data(8) And &H80) >> 7
        End Sub

        Sub New(year As UInt16, month As Byte, day As Byte, dayofweek As Enums.EIBData_Days, hour As Byte, minute As Byte, second As Byte, fault As Enums.EIBData_DateFault, workingDay As Enums.EIBData_WorkingDay, workingDayValid As Enums.EIBData_Valid, yearValid As Enums.EIBData_Valid, monthDayValid As Enums.EIBData_Valid, dayOfWeekValid As Enums.EIBData_Valid, timeValid As Enums.EIBData_Valid, time As Enums.EIBData_Time, clockQuality As Enums.EIBData_ClockQuality)
            If year < 1900 OrElse year > 1900 + 255 Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If month = 0 OrElse month > 12 Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If day = 0 OrElse day > 31 Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_Days), dayofweek) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If hour > 24 Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If minute > 59 Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If second > 59 Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_DateFault), fault) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_WorkingDay), workingDay) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_Valid), workingDayValid) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_Valid), yearValid) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_Valid), monthDayValid) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_Valid), dayOfWeekValid) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_Valid), timeValid) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_Time), time) Then Throw New EIB.Exceptions.EIBDataEncodeException("")
            If Not [Enum].IsDefined(GetType(Enums.EIBData_ClockQuality), clockQuality) Then Throw New EIB.Exceptions.EIBDataEncodeException("")

            _year = year
            _month = month
            _day = day
            _dayOfWeek = dayofweek
            _hour = hour
            _minute = minute
            _second = second
            _fault = fault
            _workingDay = workingDay
            _workingDayValid = workingDayValid
            _yearValid = yearValid
            _monthDayValid = monthDayValid
            _dayOfWeekValid = dayOfWeekValid
            _timeValid = timeValid
            _time = time
            _clockQuality = clockQuality
            MyBase._data = New Byte() {0,
                                       (_year - 1900),
                                       _month And &HF,
                                       _day And &H1F,
                                       ((_dayOfWeek And &H7) << 5) + (_hour And &H1F),
                                       _month And &H3F,
                                       _second And &H3F,
                                       ((_fault And &H1) << 7) + ((_workingDay And &H1) << 6) + ((_workingDayValid And &H1) << 5) + ((_yearValid And &H1) << 4) + ((_monthDayValid And &H1) << 3) + ((_dayOfWeekValid And &H1) << 2) + ((_timeValid And &H1) << 1) + (_time And &H1),
                                       ((_clockQuality And &H1) << 7)
                                      }
        End Sub


        Public Overrides Function CanEncode(data() As Byte) As Boolean
            Return data IsNot Nothing AndAlso data.Length = DPT_OctetLength
        End Function

        Public Overrides Function ToString() As String
            If _timeValid AndAlso _yearValid AndAlso _monthDayValid AndAlso _dayOfWeekValid Then
                Return String.Format("{0} {1}.{2}.{3} {4}:{5}:{6}", _dayOfWeek, _day, _month, _year, _hour, _minute, _second)
            ElseIf _timeValid AndAlso _yearValid AndAlso _monthDayValid Then
                Return String.Format("{0}.{1}.{2} {3}:{4}:{5}", _day, _month, _year, _hour, _minute, _second)
            ElseIf _timeValid AndAlso _monthDayValid Then
                Return String.Format("{0}.{1} {2}:{3}:{4}", _day, _month, _hour, _minute, _second)
            ElseIf _timeValid Then
                Return String.Format("{0}:{1}:{2}", _hour, _minute, _second)
            ElseIf _yearValid AndAlso _monthDayValid AndAlso _dayOfWeekValid Then
                Return String.Format("{0} {1}.{2}.{3}", _dayOfWeek, _day, _month, _year)
            ElseIf _yearValid AndAlso _monthDayValid Then
                Return String.Format("{0}.{1}.{2}", _day, _month, _year)
            ElseIf _monthDayValid AndAlso _dayOfWeekValid Then
                Return String.Format("{0} {1}.{2}", _dayOfWeek, _day, _month)
            ElseIf _monthDayValid Then
                Return String.Format("{0}.{1}", _day, _month)
            Else
                Return "complex DateTime format"
            End If
        End Function

        Public Overrides Function ToObject() As Object()
            Return New Object() {_year,
                                 _month,
                                 _day,
                                 _dayOfWeek,
                                 _hour,
                                 _minute,
                                 _second,
                                 _fault,
                                 _workingDay,
                                 _workingDayValid,
                                 _yearValid,
                                 _monthDayValid,
                                 _dayOfWeekValid,
                                 _timeValid,
                                 _time,
                                 _clockQuality
                                }
        End Function

        Public Overrides ReadOnly Property ObjectType As System.Type()
            Get
                Return New Type() {GetType(UInt16),
                                   GetType(Byte),
                                   GetType(Byte),
                                   GetType(Enums.EIBData_Days),
                                   GetType(Byte),
                                   GetType(Byte),
                                   GetType(Byte),
                                   GetType(Enums.EIBData_DateFault),
                                   GetType(Enums.EIBData_WorkingDay),
                                   GetType(Enums.EIBData_Valid),
                                   GetType(Enums.EIBData_Valid),
                                   GetType(Enums.EIBData_Valid),
                                   GetType(Enums.EIBData_Valid),
                                   GetType(Enums.EIBData_Valid),
                                   GetType(Enums.EIBData_Time),
                                   GetType(Enums.EIBData_ClockQuality)
                                  }
            End Get
        End Property

        Public Overrides Function ToInfoList() As List(Of EIB.InfoBase)
            Dim list As New List(Of EIB.InfoBase)

            list.Add(New EIB.Info("Type", DPT_Name))
            list.Add(New EIB.Info("Year", _year))
            list.Add(New EIB.Info("Month", _month))
            list.Add(New EIB.Info("Day", _day))
            list.Add(New EIB.Info("DayOfWeek", _dayOfWeek))
            list.Add(New EIB.Info("Hour", _hour))
            list.Add(New EIB.Info("Minute", _minute))
            list.Add(New EIB.Info("Second", _second))
            list.Add(New EIB.Info("Fault", String.Format("{0} (0x{0:X})", _fault)))
            list.Add(New EIB.Info("WorkingDay", String.Format("{0} (0x{0:X})", _workingDay)))
            list.Add(New EIB.Info("WorkingDayValid", String.Format("{0} (0x{0:X})", _workingDayValid)))
            list.Add(New EIB.Info("YearValid", String.Format("{0} (0x{0:X})", _yearValid)))
            list.Add(New EIB.Info("MonthDayValid", String.Format("{0} (0x{0:X})", _monthDayValid)))
            list.Add(New EIB.Info("DayOfWeekValid", String.Format("{0} (0x{0:X})", _dayOfWeekValid)))
            list.Add(New EIB.Info("TimeValid", String.Format("{0} (0x{0:X})", _timeValid)))
            list.Add(New EIB.Info("Time", String.Format("{0} (0x{0:X})", _time)))
            list.Add(New EIB.Info("Quality", String.Format("{0} (0x{0:X})", _clockQuality)))

            Return list
        End Function

        Public Shared Shadows ReadOnly Property DPT_BaseName As String
            Get
                Return "U8r4U4r3U5U3U5r2U6r2U6B16"
            End Get
        End Property

        Public Shared Shadows ReadOnly Property DPT_OctetLength As Byte
            Get
                Return 9
            End Get
        End Property

        Public Shared Shadows ReadOnly Property DPT_Name As String
            Get
                Return "DPT_19"
            End Get
        End Property

        Public Shared Shadows ReadOnly Property DPT_Number As String
            Get
                Return "19.xxx"
            End Get
        End Property


        Public Shared Function FromDateTime(value As DateTime) As EIBDataDPT_19_001
            Return New EIBDataDPT_19_001(value.Year, value.Month, value.Day, Enums.DayOfWeekToDay(value.DayOfWeek), value.Hour, value.Minute, value.Second, Enums.EIBData_DateFault.No_Fault, Enums.EIBData_WorkingDay.No_working_day, Enums.EIBData_Valid.Invalid, Enums.EIBData_Valid.Valid, Enums.EIBData_Valid.Valid, Enums.EIBData_Valid.Valid, Enums.EIBData_Valid.Valid, If(value.IsDaylightSavingTime, Enums.EIBData_Time.summer_daylight_saving_time, Enums.EIBData_Time.standard_time), Enums.EIBData_ClockQuality.wo_ext__sync)
        End Function

        Public ReadOnly Property ToDateTime As DateTime
            Get
                If _yearValid AndAlso _monthDayValid AndAlso _timeValid Then
                    Return New DateTime(_year, _month, _day, _hour, _minute, _second)
                ElseIf _yearValid AndAlso _monthDayValid Then
                    Return New DateTime(_year, _month, _day)
                ElseIf _monthDayValid AndAlso _timeValid Then
                    Return New DateTime(1, _month, _day, _hour, _minute, _second)
                ElseIf _monthDayValid Then
                    Return New DateTime(1, _month, _day)
                ElseIf _timeValid Then
                    Return New DateTime(1, 1, 1, _hour, _minute, _second)
                Else
                    Return Nothing
                End If
            End Get
        End Property

        Public ReadOnly Property Year As UInt16
            Get
                Return _year
            End Get
        End Property

        Public ReadOnly Property Month As Byte
            Get
                Return _month
            End Get
        End Property

        Public ReadOnly Property Day As Byte
            Get
                Return _day
            End Get
        End Property

        Public ReadOnly Property DayOfWeek As Enums.EIBData_Days
            Get
                Return _dayOfWeek
            End Get
        End Property

        Public ReadOnly Property Hour As Byte
            Get
                Return _hour
            End Get
        End Property

        Public ReadOnly Property Minute As Byte
            Get
                Return _minute
            End Get
        End Property

        Public ReadOnly Property Second As Byte
            Get
                Return _second
            End Get
        End Property

        Public ReadOnly Property Fault As Enums.EIBData_DateFault
            Get
                Return _fault
            End Get
        End Property

        Public ReadOnly Property WorkingDay As Enums.EIBData_WorkingDay
            Get
                Return _workingDay
            End Get
        End Property

        Public ReadOnly Property WorkingDayValid As Enums.EIBData_Valid
            Get
                Return _workingDayValid
            End Get
        End Property

        Public ReadOnly Property YearValid As Enums.EIBData_Valid
            Get
                Return _yearValid
            End Get
        End Property

        Public ReadOnly Property MonthDayValid As Enums.EIBData_Valid
            Get
                Return _monthDayValid
            End Get
        End Property

        Public ReadOnly Property DayOfWeekValid As Enums.EIBData_Valid
            Get
                Return _dayOfWeekValid
            End Get
        End Property

        Public ReadOnly Property TimeValid As Enums.EIBData_Valid
            Get
                Return _timeValid
            End Get
        End Property

        Public ReadOnly Property Time As Enums.EIBData_Time
            Get
                Return _time
            End Get
        End Property

        Public ReadOnly Property ClockQuality As Enums.EIBData_ClockQuality
            Get
                Return _clockQuality
            End Get
        End Property
    End Class
End Namespace